const {
  OPCUAClient,
  MessageSecurityMode,
  SecurityPolicy,
  AttributeIds,
  ClientSubscription,
  TimestampsToReturn,
  ClientMonitoredItem,
  DataType
} = require('./../node/node-opcua/index');

const dfOpcuaList = [];

const doList = []; // writeList

let doPending = false;

let opcua = null;

class dfOpcua {
  options = null;

  reNum = 5;

  sock = null;

  sOptions = null;

  // sockPing = false;

  isInit = false;

  num = 0;

  overTime = 100;

  // pending = false;

  name = null;

  isOnLine = false;

  connectType = null;

  plcId = null;

  addressObj = null;

  readList = null;

  session = null;

  constructor(options) {
    this.options = options;

    // const { opcuaOptions = null } = options;

    if (options) {
      const {
        connectOverTime = 100,
        reConnectNum = 5,
        name = null,
        plcId = null,
        addressObj = null,
        addressList = []
      } = options;

      this.isInit = true;

      if (!this.sOptions) {
        this.sOptions = options;
      }

      if (connectOverTime) {
        this.overTime = connectOverTime;
      }

      if (reConnectNum) {
        this.reNum = reConnectNum;
      }

      if (name) {
        this.name = name;
      }

      if (plcId) {
        this.plcId = plcId;
      }

      if (addressObj) {
        this.addressObj = addressObj;
      }

      if (addressList && addressList.length) {
        this.readList = addressList;
      }

      this.initFn(this.options);

      // dfopcuaList.push(this);
    } else {
      throw new Error('请传入配置参数');
    }
  }

  async initFn(options = this.options) {
    const {
      host = null,
      userName = null,
      password = null,
      name = null
    } = options;

    try {
      const client = OPCUAClient.create({
        applicationName: name,
        connectionStrategy: {
          initialDelay: 1000,
          maxRetry: 1
        },
        securityMode: MessageSecurityMode.None,
        securityPolicy: SecurityPolicy.None,
        endpointMustExist: false,
        userName,
        password
      });

      await client.connect(host);

      console.log('connected !');

      process.send({
        type: 'isOnline',
        success: true,
        name: this.name
      });

      this.isOnLine = true;

      const session = await client.createSession();

      this.session = session;

      this.sock = client;

      this.readFn();
    } catch (e) {
      console.log(e);
    }
  }

  reConnectFn(options = this.options, isConnent = false) {
    if (!this.isInit || isConnent) {
      if (!this.isInit) {
        this.isInit = true;
      }

      this.sock = null;

      this.initFn(options);

      process.send({
        type: 'isOnline',
        success: false,
        name: this.name
      });

      this.isOnLine = false;
    }
  }

  async readFn() {
    // console.log(this)
    if (this.sock) {
      try {
        // const dataValue2 = await session.read({
        //   nodeId: 'ns=2;s=通道 1.设备 1.标记 2',
        //   attributeId: AttributeIds.Value
        // });
        // console.log(dataValue2);
        // console.log(' value = ', dataValue2.toString());
        if (this.readList && this.readList.length) {
          try {
            const list = this.readList.map(item => ({
              nodeId: item.nodeId,
              attributeId: AttributeIds.Value
            }));

            this.session.read(list).then(valueList => {
              console.log(valueList);

              if(valueList && valueList.length){
                let data = null;

                valueList.forEach((item,key)=>{
                  if (this.readList[key]['funId']) {
                    if (!data) {
                      data = {};
                    }

                    data[this.readList[key]['funId']] = item?.value?.value || 0;
                  }
                })

                if(data){
                  process.send({
                    type: 'read',
                    success: true,
                    name: this.name,
                    data
                  });
                }
              }

              for(const item of this.readList){
                const subscription = ClientSubscription.create(this.session, {
                  requestedPublishingInterval: 1000,
                  requestedLifetimeCount: 100,
                  requestedMaxKeepAliveCount: 10,
                  maxNotificationsPerPublish: 100,
                  publishingEnabled: true,
                  priority: 10
                });

                subscription
                  .on('started', function () {
                    console.log(
                      'subscription started for 2 seconds - subscriptionId=',
                      subscription.subscriptionId
                    );
                  })
                  .on('keepalive', function () {
                    console.log('keepalive');
                  })
                  .on('terminated', function () {
                    console.log('terminated');
                  });

                  const monitoredItem = ClientMonitoredItem.create(
                    subscription,
                    {
                      nodeId: item.nodeId,
                      attributeId: AttributeIds.Value
                    },
                    {
                      samplingInterval: 100,
                      discardOldest: true,
                      queueSize: 10
                    },
                    TimestampsToReturn.Both
                  );

                  monitoredItem.on('changed', dataValue => {
                    process.send({
                      type: 'read',
                      success: true,
                      name: this.name,
                      data: { [item.funId]: dataValue?.value.value }
                    });
                  });
              }
            });
          } catch (e) {
            console.log(e);
          }
        }
      } catch (e) {
        console.log(e);
      }
    } else {
      return false;
    }
  }

  async commandFn(list, writeId = null) {
    if (this.sock && this.session && list && list.length) {

      const writeList = [];

      let writeStatus = null;

      for(const item of list){
        const { nodeId =null,value=null,type=null} = item;

        writeList.push({
          nodeId,
          attributeId: AttributeIds.Value,
          value: {
            value: {
              dataType: DataType[type],
              value
            }
          }
        });
      }

      this.session.write(writeList, (err, statusCodes)=> {
        if (err) {
          // console.log('写入操作失败', err);
          writeStatus = false;

          process.send({
            type: 'write',
            success: false,
            name: this.name,
            err: `写入失败 ${statusCodes}`,
            msg: err,
            id: writeId
          });
        } else {
          // console.log('写入操作成功');

          writeStatus = true;

          process.send({
            type: 'write',
            success: true,
            name: this.name,
            id: writeId
          });
        }
      });

      // sock.resume();
      // console.log(data)
      return new Promise(resolve => {
        let num = 0;

        let sockPingCommand = setInterval(async () => {
          num += 100;

          if (writeStatus !== null || num >= this.overTime) {
            clearInterval(sockPingCommand);

            console.log(writeStatus);

            // this.pending = false;

            resolve(!!writeStatus);
          }
        }, 100);
      });
    } else {
      return false;
    }
  }

  async setReadFn(list = [], type = 'add') {
    if (this.sock && list && list.length) {
      if (type === 'add') {
        this.sock.addItems(list);
      }

      if (type === 'remove') {
        this.sock.removeItems(list);
      }

      return true;
    } else {
      return false;
    }
  }

  closeSockFn() {
    if (this.sock) {
      this.sock.disconnect();
    }
  }
}

const initFn = async data => {
  if (data) {
    const { options } = data;

    opcua = new dfOpcua(options);

    dfOpcuaList.push(opcua);
  }
};

const deleteFn = () => {
  console.log('关闭');

  // if(intervalTime){
  //   clearInterval(intervalTime);

  //   intervalTime = null;
  // }

  if (opcua && opcua.closeSockFn) {
    opcua.closeSockFn();
  }

  opcua = null;

  process.exit();
};

const writeFn = async obj => {
  const { isOnLine = false, sock = null, session=null } = opcua || {};

  if (isOnLine && sock && session && obj) {
    const { writeObj = null, id = null } = obj;

    if (doPending) {
      doList.push(obj);

      return false;
    } else {
      doPending = true;
    }

    const res = await opcua.commandFn(writeObj, id);

    doPending = false;

    if (res === false) {
      console.log(res);
      // fn && fn(res);

      // return false;
    }

    if (doList && doList.length) {
      writeFn(doList[0]);

      doList.splice(0, 1);
    }
    // console.log(121212)
  }
};

const connectFn = () => {
  for (const item of dfOpcua) {
    const {
      isOnLine = false,
      num = 0,
      reNum = 0,
      reConnectFn = null,
      sOptions = null,
      name = null
    } = item;

    if (!isOnLine && reConnectFn && sOptions && num > reNum) {
      item.num = 0;

      console.log(name + '重连');

      setTimeout(() => {
        item.reConnectFn(sOptions, true);
      }, 1000);
    }
  }
};

process.on('message', obj => {
  if (obj) {
    const { type = null, data = null } = obj;

    switch (type) {
      case 'init':
        initFn(obj);
        break;

      case 'write':
        writeFn(data);
        break;

      case 'connect':
        connectFn();
        break;

      case 'delete':
        deleteFn();
        break;

      default:
        break;
    }
  }
});
